using Assets.Scripts;
using Assets.Scripts.Inventory;
using Assets.Scripts.Localization2;
using Assets.Scripts.Objects.Electrical;
using Assets.Scripts.Objects.Items;
using Assets.Scripts.Serialization;
using Assets.Scripts.UI;
using HarmonyLib;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Serialization;
using TMPro;
using UI.Tooltips;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;
using VisualmodBep;

namespace StationeersServerPreference {
    // foreach (FieldInfo f in AccessTools.GetDeclaredFields(typeof(Localization))) { Log.LogInfo($"{f.Name} {f.IsPrivate} {f.IsStatic}"); }
    public class SspLocalization {
        static readonly string XmlPath = $"BepInEx/plugins/{PluginInfo.PLUGIN_NAME}/Language";
        static readonly string NameSuffix = "-ssp_override";

        public static void Run(bool loadXml = true, bool forceSet = true) {
            Log.LogInfo($"SspLocalization loadXml={loadXml} forceSet={forceSet}");
            if (loadXml) {
                foreach (Localization.LanguageFolder folder in Localization.LanguageData.Values) {
                    if (folder != null) {
                        // Log.LogInfo($"  folder={folder.Code} pages={folder.LanguagePages.Count}");
                        folder.LanguagePages.RemoveAll(page => !string.IsNullOrEmpty(page.Name) && page.Name.EndsWith(NameSuffix)); // for some pages, Name == null
                    }
                }
                if (!LoadXml()) {
                    return;
                }
            }
            if (forceSet) {
                Localization.SetLanguage(Localization.CurrentLanguage, true);
                SspGui.Instance.UpdatePrefabDisplayName();
            }
        }

        public static bool LoadXml() {
            if (!Directory.Exists(XmlPath)) {
                Log.LogError($"SspLocalization LoadXml \"{XmlPath}\" not exists.");
                return false;
            }
            string[] files = Directory.GetFiles(XmlPath, "*.xml");
            Log.LogInfo($"SspLocalization LoadXml {files.Length} files");
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Localization.Language));
            foreach (string text in files) {
                Localization.Language page = XmlSerialization.Deserialize(xmlSerializer, text) as Localization.Language;
                if (page == null) {
                    Log.LogError($"  parsing error: {text}.");
                    continue;
                }
                string stat = $"{page.Code} Things={page.Things.Count} Keys={page.Keys.Count} Interface={page.Interface.Count}";
                page.Name = (page.Name ?? "") + NameSuffix; // flag

                Localization.LanguageFolder folder;
                Localization.LanguageData.TryGetValue(page.Code, out folder);
                if (folder == null) {
                    Localization.LanguageData.Add(page.Code, new Localization.LanguageFolder(page));
                    Log.LogInfo("  new " + stat);
                } else {
                    folder.LanguagePages.Add(page);
                    Log.LogInfo("  add " + stat);
                }
            }
            return true;
        }

        public static TMP_FontAsset CurrentFont {
            get {
                return Localization.CurrentFont ?? InventoryManager.Instance?.DefualtFont;
            }
        }

        private static string[] _roboto_names = { "Roboto", "Roboto Mono", "Microsoft YaHei" };
        private static Font _roboto = null;

        public static Font RobotoFont {
            get {
                if (_roboto == null) {
                    string[] fonts = Font.GetOSInstalledFontNames();
                    // Log.LogInfo(string.Join(", ", fonts));
                    foreach (string fontName in _roboto_names) {
                        if (fonts.Contains(fontName)) {
                            _roboto = Font.CreateDynamicFontFromOSFont(fontName, 28);
                            return _roboto;
                        }
                    }
                    _roboto = Font.CreateDynamicFontFromOSFont("Arial", 28); // should exists
                }
                return _roboto;
            }
        }

        public static bool IsOverrideFont {
            get {
                return !Plugin.IsDedicatedServer && SspConfig.OverrideLocalization.Value;
            }
        }

        public static bool ChangeTMProFont(TextMeshProUGUI tmpro, TMP_FontAsset font = null) {
            if (tmpro == null) {
                return false;
            }
            if (font == null) {
                font = SspLocalization.CurrentFont;
            }
            if (tmpro.font != font) {
                tmpro.font = font; // change font
                return true;
            }
            return false;
        }

        public static void TryFixScreenFont(Console console, string logPrefix) {
            logPrefix += ' ' + console.DisplayName;
            if (SspLocalization.RobotoFont == null) {
                Log.LogError($"{logPrefix} no available fonts");
                return;
            }
            GameObject screen = console.Screen;
            if (screen == null || console.CurrentMotherboard == null) {
                Log.LogInfo($"{logPrefix} no Screen or mb");
                return;
            }
            GameObject displayName = screen.transform.Find("PanelNormal/DisplayName")?.gameObject ?? screen.transform.Find("PanelNormal/Title")?.gameObject;
            if (displayName == null) {
                Log.LogError($"{logPrefix} no DisplayName or Title");
                return;
            }
            Text text = displayName.GetComponent<Text>();
            Log.LogInfo($"{logPrefix} font {text.font.name}");
            if (text.font.name == "ARIALBD") {
                text.font = SspLocalization.RobotoFont;
            }
        }
    }

    namespace HarmonyPatcher {
        public class PatcherLocalization {
            [HarmonyPatch(typeof(Localization), nameof(Localization.GetLanguages))]
            [HarmonyPostfix]
            static void Patch_Localization_GetLanguages(string path) {
                string p = path.Replace('\\', '/');
                if (p.StartsWith(Application.streamingAssetsPath)) {
                    p = p.Substring(Application.streamingAssetsPath.Length);
                }
                Log.LogInfo($"POST Localization.GetLanguages \"{p}\"");
                if (p == "" && SspConfig.OverrideLocalization.Value) {
                    SspLocalization.Run(true, false); // initialize
                }
            }

            /** fix CJK font issues for In-Game Menu Buttons */
            [HarmonyPatch(typeof(InGameMenu), nameof(InGameMenu.OnEnable))]
            [HarmonyPrefix]
            static void Patch_InGameMenu_OnEnable(InGameMenu __instance) {
                if (SspLocalization.IsOverrideFont) {
                    TextMeshProUGUI button = AccessTools.FieldRefAccess<InGameMenu, TextMeshProUGUI>(__instance, "_saveButtonText");
                    if (button != null && SspLocalization.ChangeTMProFont(button)) {
                        button.text = GameStrings.SaveButtonGameString.DisplayString;
                        Log.LogInfo("PREF InGameMenu.OnEnable _saveButtonText");
                    }
                    button = AccessTools.FieldRefAccess<InGameMenu, TextMeshProUGUI>(__instance, "_saveAsButtonText");
                    if (button != null && SspLocalization.ChangeTMProFont(button)) {
                        Log.LogInfo("PREF InGameMenu.OnEnable _saveAsButtonText");
                    }
                }
            }
            /**
             * fix CJK font issues for Config Cartridge
             * @ref NetworkAnalyser.Start
             */
            [HarmonyPatch(typeof(ConfigCartridge), nameof(ConfigCartridge.OnScreenUpdate))]
            [HarmonyPrefix]
            static void Patch_ConfigCartridge_OnScreenUpdate(ConfigCartridge __instance) {
                if (SspLocalization.IsOverrideFont) {
                    if (SspLocalization.ChangeTMProFont(__instance.SelectedTitle)) {
                        Log.LogInfo("PREF ConfigCartridge.OnScreenUpdate SelectedTitle.font");
                    }
                }
            }

            /**
             * fix CJK font issues for Code Line
             * @ref EditorLineOfCode.ReformatText
             */
            [HarmonyPatch(typeof(EditorLineOfCode), nameof(EditorLineOfCode.TextChanged))]
            [HarmonyPrefix]
            static void Patch_EditorLineOfCode_TextChanged(EditorLineOfCode __instance) {
                if (SspLocalization.IsOverrideFont) {
                    TMP_FontAsset font = SspLocalization.CurrentFont;
                    if (!__instance.FormattedText.font.fallbackFontAssetTable.Contains(font)) {
                        Log.LogInfo("PREF EditorLineOfCode.TextChanged FormattedText.font.fallbackFontAssetTable"); // change font
                        __instance.FormattedText.font.fallbackFontAssetTable.Add(font);
                    }
                }
            }

            /**
             * fix CJK font issues for Code Tooltip
             * @ref EditorLineOfCode.ReformatText
             */
            [HarmonyPatch(typeof(UITooltipPanel), "UpdateLayout")]
            [HarmonyPrefix]
            static void Patch_UITooltipPanel_UpdateLayout(UITooltipPanel __instance) {
                if (SspLocalization.IsOverrideFont) {
                    FieldInfo field = AccessTools.Field(typeof(UITooltipPanel), "_textMesh"); // private field
                    if (field != null) {
                        TextMeshProUGUI tmpro = field.GetValue(__instance) as TextMeshProUGUI;
                        if (SspLocalization.ChangeTMProFont(tmpro)) {
                            Log.LogInfo("PREF UITooltipPanel.UpdateLayout _textMesh.font");
                        }
                    }
                }
            }

            /**
             * fix CJK font issues for Display
             * @ref Console.OnRegistered
             */
            [HarmonyPatch(typeof(Computer), nameof(Computer.OnFinishedLoad))]
            [HarmonyPostfix]
            static void Patch_Computer_OnFinishedLoad(Computer __instance) {
                Console console = __instance as Console;
                if (console != null && SspLocalization.IsOverrideFont) {
                    SspLocalization.TryFixScreenFont(console, "POST Computer.OnFinishedLoad");
                }
            }

            /**
             * fix CJK font issues for Display
             * @ref Console.OnRegistered
             */
            [HarmonyPatch(typeof(Computer), "EnableAppropriateScreen")]
            [HarmonyPostfix]
            static void Patch_Computer_EnableAppropriateScreen(Computer __instance) {
                Console console = __instance as Console;
                if (console != null && SspLocalization.IsOverrideFont) {
                    SspLocalization.TryFixScreenFont(console, "POST Computer.EnableAppropriateScreen");
                }
            }

            /**
             * fix CJK font issues for External Temperature Status (bottom-right)
             */
            [HarmonyPatch(typeof(PlayerStateWindow), "Update")]
            [HarmonyPostfix]
            static void Patch_PlayerStateWindow_Update(PlayerStateWindow __instance) {
                if (--PlayerStateWindow_Update_Countdown < 0 && SspLocalization.IsOverrideFont) {
                    PlayerStateWindow_Update_Countdown = 500;
                    if (SspLocalization.ChangeTMProFont(__instance.InfoExternalTemperature)) {
                        Log.LogInfo("POST PlayerStateWindow.Update InfoExternalTemperature.font");
                    }
                }
            }

            private static int PlayerStateWindow_Update_Countdown = 0;
        }
    }
}
